package edu.buaa.energy;


import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.aliyun.log.producer.Producer;
import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
import edu.buaa.common.TxResultProcessor;
import edu.buaa.common.client.DBProxy;
import edu.buaa.common.client.TGraphExecutorClient;
import edu.buaa.common.client.Neo4jExecutorClient;
import edu.buaa.common.transaction.AbstractTransaction;
import edu.buaa.common.transaction.CreateTGraphAggrMaxIndexTx;
import edu.buaa.common.transaction.CreateTGraphTemporalValueIndexTx;
import edu.buaa.energy.client.MariaDBClient;
import edu.buaa.energy.client.PostgreSQLClient;
import edu.buaa.energy.transaction.CreateTGraphAggrDurationIndexTx;
import edu.buaa.energy.transaction.ImportTemporalDataTx;
import edu.buaa.utils.Helper;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class MilestoneBuilder {
    private static final String dataset = Helper.mustEnv("DATASET");
    private static final String dbType = Helper.mustEnv("DB_TYPE");
    private static final String serverHost = Helper.mustEnv("DB_HOST"); // hostname of server.
    private static final String dataPath = Helper.mustEnv("DATA_PATH"); // should be like '/media/song/test/data-set/beijing-traffic/TGraph/byday/'
    private static final String tpRange = Helper.mustEnv("TP_RANGE").split("_")[1]; //20120101
    private static final String startDay = tpRange.split("-")[0];
    private static final String endDay = tpRange.split("-")[1];
    private static final boolean staticOnly = Helper.mustEnv("TP_RANGE").split("_")[0].contains(".0");

    public MilestoneBuilder(){
        System.out.println("build time set to "+startDay+" ~ "+endDay);
        assert dataset.equals("energy");
    }

    private static Producer logger;
    private static DBProxy client;
    private static TxResultProcessor post;
    private static EnergyDataTxGenerator txGen;
    private static Iterator<ImportTemporalDataTx> iterator;

    public static void main(String[] args){
        try {
            client = initClient(dbType, serverHost, 1);
            client.testServerClientCompatibility();
            String codeVersion = Helper.codeGitVersion();
            post = new TxResultProcessor(getTestName(dbType), codeVersion);
            logger = Helper.getLogger();
            post.setLogger(logger);
            importStatic();
            if(!staticOnly) importTemporal();
            close();
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    private static void importStatic() throws Exception {
        System.out.print("import static data...");
        txGen = new EnergyDataTxGenerator(new File(dataPath, dataset));
        AbstractTransaction txStaticImport = txGen.readNetwork();
        post.processSync(client.execute(txStaticImport), txStaticImport);
        if(staticOnly) return;
        iterator = txGen.readTemporalStatus(startDay, endDay, 1);
        System.out.println("done\nimport temporal data...");
        if(iterator.hasNext() && "tgraph_kernel".equals(dbType)){
            ImportTemporalDataTx tx = iterator.next();
            post.processSync(client.execute(tx), tx);
            createTGraphIndexes();
        }
    }

    private static void createTGraphIndexes() throws Exception {
        int tBegin = EnergyDataTxGenerator.parseTime(startDay);
        int tFinish = EnergyDataTxGenerator.parseTime(endDay);
        createIndex(max(EnergyDataTxGenerator.properties[0], tBegin, tFinish, 1, Calendar.DATE));
        createIndex(dur(EnergyDataTxGenerator.properties[0], tBegin, tFinish, 1, Calendar.DATE, -1F, 332.66666F, 666.3333F));
        createIndex(tv(EnergyDataTxGenerator.properties[0], tBegin, tFinish));
    }

    public static void importTemporal() throws Exception {
        long lastTick = System.currentTimeMillis();
        while (iterator.hasNext()) {
            ImportTemporalDataTx tx = iterator.next();
            if(System.currentTimeMillis()-lastTick>30_000){
                lastTick = System.currentTimeMillis();
                System.out.println(tx.data.get(0));
            }
            post.process(client.execute(tx), tx);
        }
    }

    public static void close() throws IOException, InterruptedException, ProducerException {
        System.out.println("import temporal data done.");
        txGen.close();
        client.close();
        post.close();
        logger.close();
    }

    public static DBProxy initClient(String dbType, String dbHost, int maxConnCnt) throws Exception {
        switch (dbType.toLowerCase()) {
            case "mariadb": return new MariaDBClient(dbHost, maxConnCnt);
            case "postgresql": return new PostgreSQLClient(dbHost, maxConnCnt);
            case "tgraph_kernel_e":
            case "tgraph_kernel": return new TGraphExecutorClient(dbHost, maxConnCnt);
            case "neo4j1": return new Neo4jExecutorClient(dbHost, 9833, maxConnCnt);
            case "neo4j2": return new Neo4jExecutorClient(dbHost, 9866, maxConnCnt);
            default:
                throw new UnsupportedOperationException();
        }
    }

    public static String getTestName(String dbType){
        Calendar c = Calendar.getInstance();
        return "M_"+c.get(Calendar.YEAR)+"."+(c.get(Calendar.MONTH)+1)+"."+c.get(Calendar.DAY_OF_MONTH)+"_"+
                c.get(Calendar.HOUR_OF_DAY)+":"+c.get(Calendar.MINUTE)+dbType.toLowerCase();
    }


    private static void createIndex(AbstractTransaction tx) throws Exception {
        System.out.println(JSON.toJSONString(tx));
        long begin = System.currentTimeMillis();
        post.processSync(client.execute(tx), tx);
        System.out.println("index created in "+(System.currentTimeMillis()-begin)+" ms");
        System.out.println();
    }

    private static CreateTGraphAggrMaxIndexTx max(String propName, int begin, int end, int every, int timeUnit ){
        CreateTGraphAggrMaxIndexTx tx = new CreateTGraphAggrMaxIndexTx();
        tx.setProName(propName);
        tx.setStart(begin);
        tx.setEnd(end);
        tx.setEvery(every);
        tx.setTimeUnit(timeUnit);
        return tx;
    }

    private static CreateTGraphAggrDurationIndexTx dur(String propName, int begin, int end, int every, int timeUnit, Float ... intStart ){
        CreateTGraphAggrDurationIndexTx tx = new CreateTGraphAggrDurationIndexTx();
        tx.setProName(propName);
        tx.setStart(begin);
        tx.setEnd(end);
        tx.setEvery(every);
        tx.setTimeUnit(timeUnit);
        tx.setIntervalStart(Arrays.asList(intStart));
        return tx;
    }

    private static CreateTGraphTemporalValueIndexTx tv(String propName, int begin, int end){
        CreateTGraphTemporalValueIndexTx tx = new CreateTGraphTemporalValueIndexTx();
        tx.setProps(Collections.singletonList(propName));
        tx.setStart(begin);
        tx.setEnd(end);
        return tx;
    }
}
